﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace SomeGame.GUI.Widgets.Containers {

    public abstract class Container : Widget {

        public readonly IList<Widget> Children = new List<Widget>();

        protected Container() {
            // ...
        }

        protected Container(Texture2D texture,
            int width,
            int height,
            Color color,
            Display display = Display.Inline,
            Position position = Position.Relative) : base(texture, width, height, color, display, position) {
            // ...
        }

        protected Container(Texture2D texture,
            Rectangle bounds,
            Color color,
            Display display,
            Position position = Position.Relative)
            : base(texture, bounds.Width, bounds.Height, color, display, position) {
            Bounds = new Rectangle(bounds.X, bounds.Y, Bounds.Width, Bounds.Height);
        }

        protected Container(Texture2D texture, Color color, Display display, Position position = Position.Relative)
            : base(texture, 0, 0, color, display, position) {
            // ...
        }

        public void AddWidget(Widget child) {
            child.Screen = Screen;
            child.Parent = this;
            Children.Add(child);
        }

        public void Layout() {

            // Don't do anything at all if it's not there.
            if (Display == Display.None) {
                return;
            }

            // All child containers must be laid out first.
            foreach (Container container in Children.OfType<Container>()) {
                container.Layout();
            }

            // BlockContainers with no width size to 100% and this must happen before it's children are laid out.
            if (this is BlockContainer && Width == 0 && Parent != null) {
                Width = Parent.Width-Parent.Padding.Left-Parent.Padding.Right;
            }

            // Used for iterating through children.
            Widget previousChild = null;

            // Relative Children.
            // TODO: Replace all the 'Bounds.Width' with 'Width'. Same for Height.
            IList<Widget> relativePositionedChildren =
                Children.Where(x => x.Position == Position.Relative && x.Display != Display.None).ToList();
            for (int i = 0; i < relativePositionedChildren.Count; i++) {
                Widget child = relativePositionedChildren[i];
                Rectangle temp = child.Bounds;
                if (previousChild != null) {
                    if (previousChild.Display == Display.Block) {
                        switch (child.HorizontalAlignment) {
                            case HorizontalAlignment.Center:
                                if (child.Width == 0 && !(child is Container)) {
                                    throw new Exception(
                                        "HorizontallyCentered non-container widgets MUST have a width set!");
                                }
                                temp.X = Bounds.X+Padding.Left+(Bounds.Width-child.Bounds.Width)/2;
                                break;
                            case HorizontalAlignment.Left:
                                temp.X = Bounds.X+Padding.Left+child.Margin.Left;
                                break;
                            case HorizontalAlignment.Right:
                                temp.X = Bounds.Right-Padding.Right-child.Margin.Right-child.Bounds.Width;
                                break;
                        }
                        temp.Y = previousChild.Bounds.Bottom+child.Margin.Top;
                    } else if (previousChild.Display == Display.Inline) {
                        if (child.Display == Display.Block) {
                            switch (child.HorizontalAlignment) {
                                case HorizontalAlignment.Center:
                                    if (child.Width == 0 && !(child is Container)) {
                                        throw new Exception(
                                            "HorizontallyCentered non-container widgets MUST have a width set!");
                                    }
                                    temp.X = Bounds.X+Padding.Left+(Bounds.Width-child.Bounds.Width)/2;
                                    break;
                                case HorizontalAlignment.Left:
                                    temp.X = Bounds.X+Padding.Left+child.Margin.Left;
                                    break;
                                case HorizontalAlignment.Right:
                                    temp.X = Bounds.Right-Padding.Right-child.Margin.Right-child.Bounds.Width;
                                    break;
                            }
                            temp.Y = previousChild.Bounds.Bottom+child.Margin.Top;
                        } else if (child.Display == Display.Inline) {
                            switch (child.HorizontalAlignment) {
                                case HorizontalAlignment.Center:
                                    if (child.Width == 0 && !(child is Container)) {
                                        throw new Exception(
                                            "HorizontallyCentered non-container widgets MUST have a width set!");
                                    }
                                    temp.X = Bounds.X+Padding.Left+(Bounds.Width-child.Bounds.Width)/2;
                                    break;
                                case HorizontalAlignment.Left:
                                    temp.X = previousChild.Bounds.Right+previousChild.Margin.Right+child.Margin.Left;
                                    break;
                                case HorizontalAlignment.Right:
                                    temp.X = previousChild.Bounds.Left-previousChild.Margin.Left-child.Margin.Right-
                                             child.Bounds.Width;
                                    break;
                            }
                            temp.Y = previousChild.Bounds.Y-previousChild.Margin.Top+child.Margin.Top;
                        }
                    }
                } else {
                    switch (child.HorizontalAlignment) {
                        case HorizontalAlignment.Center:
                            temp.X = Bounds.X+Padding.Left+(Bounds.Width-child.Bounds.Width)/2;
                            temp.Y = Bounds.Y+Padding.Top+child.Margin.Top;
                            break;
                        case HorizontalAlignment.Left:
                            temp.X = Bounds.X+Padding.Left+child.Margin.Left;
                            temp.Y = Bounds.Y+Padding.Top+child.Margin.Top;
                            break;
                        case HorizontalAlignment.Right:
                            temp.X = Bounds.Right-Padding.Right-child.Margin.Right-child.Bounds.Width;
                            temp.Y = Bounds.Y+Padding.Top+child.Margin.Top;
                            break;
                    }
                }
                child.Bounds = temp;
                previousChild = child;
            }

            // Absolute Children.
            IList<Widget> absolutePositionedChildren =
                Children.Where(x => x.Position == Position.Absolute && x.Display != Display.None).ToList();
            for (int i = 0; i < absolutePositionedChildren.Count; i++) {
                Widget child = absolutePositionedChildren[i];
                Rectangle temp = child.Bounds;
                if (child.Left != null) {
                    temp.X = Bounds.Left+child.Left.Value;
                } else if (child.Right != null) {
                    temp.X = Bounds.Right-child.Bounds.Width-child.Right.Value;
                } else {
                    temp.X = Bounds.Left;
                }
                if (child.Top != null) {
                    temp.Y = Bounds.Top+child.Top.Value;
                } else if (child.Bottom != null) {
                    temp.Y = Bounds.Bottom-child.Bottom.Value-child.Bounds.Height;
                } else {
                    temp.Y = Bounds.Top;
                }
                child.Bounds = temp;
            }

            // Inline containers with zero Width are stretched to fit their children, which were set above in the loop.
            // The children were moved and now the container will enlarge itself to contain them.
            if (this is InlineContainer && Width == 0) {
                int left = Bounds.Left;
                int right = Bounds.Right;
                foreach (Widget child in Children) {
                    if (child.Bounds.Right+child.Margin.Right+Padding.Left > right) {
                        right = child.Bounds.Right+child.Margin.Right+Padding.Left;
                    }
                }
                Width = right-left;
            }

            // Containers with zero Height are stretched to fit their children, which were set above in the loop.
            // The children were moved and now the container will enlarge itself to contain them.
            if (Height == 0) {
                int top = Bounds.Top;
                int bottom = Bounds.Bottom;
                foreach (Widget child in Children) {
                    if (child.Bounds.Bottom+child.Margin.Bottom+Padding.Top > bottom) {
                        bottom = child.Bounds.Bottom+child.Margin.Bottom+Padding.Top;
                    }
                }
                Height = bottom-top;
            }
        }

        public override void Update(GameTime gameTime) {
            foreach (Widget child in Children) {
                child.Update(gameTime);
            }
        }

        public override void Draw() {
            // Draw self.
            base.Draw();
            // 
            if (Display == Display.None) {
                return;
            }
            // Draw relative children.
            foreach (Widget widget in Children.Where(x => x.Position == Position.Relative)) {
                widget.Draw();
            }
            // Draw absolute children.
            foreach (Widget widget in Children.Where(x => x.Position == Position.Absolute)) {
                widget.Draw();
            }
        }

    }

}